Implement fan-out degrade contribution (proposal 0066)#161
Merged
Conversation
Adopt proposal 0066: a degraded fan-out instance is a success whose contribution is its degraded_update, read in subgraph-field-name space. Make the per-instance partial uniformly subgraph-keyed across the extract, accumulator, resume, and fan-in paths, so a degrade's subgraph-space degraded_update composes through the same fan-in. This fixes a latent bug where an instance degraded_update's extra_outputs values were looked up by parent field name and silently dropped. A static degraded_update on an instance FailureIsolationMiddleware that omits collect_field is now a compile-time error (FanOutDegradedUpdateMissingCollectField); a callable degraded_update that omits it yields a graceful null slot rather than raising. Advance the pinned spec to v0.56.0, mark 0066 implemented, wire fixture 065 (four cases), and sync the spec-version declarations.
There was a problem hiding this comment.
Pull request overview
Implements openarmature-spec proposal 0066 (spec v0.56.0) by updating fan-out failure-isolation degrade semantics so a degraded instance is treated as a success whose contribution is its degraded_update in subgraph-field-name space, and updates the conformance harness + pins to v0.56.0.
Changes:
- Fix fan-out
extra_outputshandling to consistently read per-instance partials in subgraph space and project into parent fields during fan-in (including resume/rolled-forward paths). - Add a compile-time validation error (
FanOutDegradedUpdateMissingCollectField) for static per-instancedegraded_updatemappings that omitcollect_field. - Extend conformance runner/type adapter to support fixture 065 patterns (
expected_compile_error,anytype), and bump spec version pins/docs.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_smoke.py | Updates spec version assertion to v0.56.0. |
| tests/conformance/test_pipeline_utilities.py | Adds compile-error case handling and includes fixture 065 in the driven set. |
| tests/conformance/adapter.py | Adds any fixture type support for null-slot collection cases. |
| src/openarmature/graph/fan_out.py | Aligns per-instance partials and fan-in/out logic with proposal 0066 (subgraph-space partials, .get for null slots). |
| src/openarmature/graph/errors.py | Introduces FanOutDegradedUpdateMissingCollectField compile error category. |
| src/openarmature/graph/builder.py | Adds compile-time check for static degraded_update missing collect_field. |
| src/openarmature/graph/init.py | Re-exports the new compile error in the public API. |
| src/openarmature/AGENTS.md | Updates referenced spec version to v0.56.0. |
| src/openarmature/init.py | Bumps __spec_version__ to v0.56.0. |
| pyproject.toml | Bumps [tool.openarmature].spec_version to v0.56.0. |
| conformance.toml | Pins spec to v0.56.0 and marks proposal 0066 as implemented. |
| CHANGELOG.md | Documents proposal 0066 behavior and the spec pin bump to v0.56.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
chris-colinsky
added a commit
that referenced
this pull request
Jun 18, 2026
The fan-out-with-retry degrade mode supplied its extra_outputs value
under the parent field name (`topics`) in degraded_update, but
proposal 0066 reads degraded_update in the subgraph's field-name space.
The subgraph field is `topic` (extra_outputs={topics: topic}), so the
parent-keyed entry was ignored, leaving the slot null (0069) and
tripping BatchState.topics: list[str] validation in degrade mode.
Key the degraded value by the subgraph field `topic` (the doc
walkthrough already documents `topic: other`). Caught running
MODE=degrade against the v0.14.0rc1 artifact; the example smoke test
only exercises fail_fast, so it slipped through #161.
chris-colinsky
added a commit
that referenced
this pull request
Jun 18, 2026
* chore(release): v0.14.0
Real release of v0.14.0 (retry & reliability; pinned spec v0.53.0 ->
v0.60.0). Bump the package version from 0.14.0rc1 to 0.14.0 across
pyproject, __version__, test_smoke, and the lockfile; regenerate the
bundled AGENTS.md version stamp. The CHANGELOG [0.14.0] section is
already dated 2026-06-17 (no drift). Tagging v0.14.0 publishes to PyPI
and creates the GitHub Release.
rc1 verified on TestPyPI (install, version, [otel] import, hello-world
against a live LLM).
* Fix degrade extra-outputs key in fan-out example
The fan-out-with-retry degrade mode supplied its extra_outputs value
under the parent field name (`topics`) in degraded_update, but
proposal 0066 reads degraded_update in the subgraph's field-name space.
The subgraph field is `topic` (extra_outputs={topics: topic}), so the
parent-keyed entry was ignored, leaving the slot null (0069) and
tripping BatchState.topics: list[str] validation in degrade mode.
Key the degraded value by the subgraph field `topic` (the doc
walkthrough already documents `topic: other`). Caught running
MODE=degrade against the v0.14.0rc1 artifact; the example smoke test
only exercises fail_fast, so it slipped through #161.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adopts proposal 0066 (spec v0.56.0): a degraded fan-out instance is a success whose contribution is its
degraded_update, read in subgraph-field-name space. The submodule advances v0.55.1 to v0.56.0 andconformance.tomlmarks 0066 implemented.Engine
extra_outputskeying fix. The per-instance partial is now uniformly subgraph-keyed across_extract_instance_partial, the accumulator,_rolled_forward_partial(resume), and both_fan_in_*functions, so a degrade's subgraph-spacedegraded_updatecomposes through the same fan-in. This fixes a latent bug: an instancedegraded_update'sextra_outputsvalues were looked up by the parent field name and silently dropped (collect_fieldwas unaffected, which is why fixture 064 with noextra_outputsdidn't surface it).degraded_updateon a fan-out instanceFailureIsolationMiddlewarethat omitscollect_fieldnow raisesFanOutDegradedUpdateMissingCollectField. A callabledegraded_updateis exempt (its output isn't knowable at construction).degraded_updatethat omitscollect_fieldyields a graceful null slot (the fan-in reads via.get), never a degrade-time raise; the positional collection slot is preserved.Harness
Fixture
065-fan-out-failure-isolation-degrade-contribution(four cases) is wired into the pipeline-utilities runner: the runner gainedexpected_compile_errorhandling for a cases-shapegraph:block, and the adapter gained theanyfixture type for Case 3'slist<any>null-slot collection.Scope
Success-path and resume behavior for correctly-configured fan-outs is unchanged; the existing fan-out and checkpoint fixtures stay green. The parallel-branches counterpart (Case 4) was already correct from the prior parallel-branches fix and is now pinned by fixture 065.
Validation
uv run pytest tests/: 1301 passed, 376 skippeduv run pytest "tests/conformance/" -k 065: four cases passuv run ruff check .,uv run pyright: cleanuv run python scripts/check_conformance_manifest.py: 61/61uv run mkdocs build: clean